package tau.dimes.visualization.applet;

import java.io.IOException;
import java.net.URL;

import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PImage;
import tau.dimes.visualization.applet.aviRecord.AVIRecorder;
import tau.dimes.visualization.applet.data.TraceDataSet;

/**
 * Main applet class.
 */
public class DimesVApplet extends PApplet
{
	private static final long serialVersionUID = 6094387357319739458L;

	/**
	 * The leftmost angle
	 */
	float leftAngle = -180;
	
	/**
	 * The rightmost angle.
	 */
	float rightAngle = 180;
	
	/**
	 * The bottom angle.
	 */
	float bottomAngle = -90;
	
	/**
	 * The top angle.
	 */
	float topAngle = 90;

	/**
	 * Grid background.
	 */
	CoordinatesBackground bg;
	
	/**
	 * HTTP request.
	 */
	HttpRequest request;

	/**
	 * XML parser
	 */
	XmlParser xmlParser;

	/**
	 * Background image.
	 */
	PImage bgImage;
	
	/**
	 * Font used to render text.
	 */
	PFont smallFont;

	/**
	 * String for holding current stage.
	 */
	String stage = "";
	
	/**
	 * Indicates whether initialization is done.
	 */
	boolean initialized = false;
	
	/**
	 * Indicates whether clock has started.
	 */
	boolean started = false;

	/**
	 * Internal applet clock.
	 */
	Clock clock;

	/**
	 * Selected visualization implementation.
	 */
	IVisualization visualization;

	/**
	 * AVI recorder.
	 */
	AVIRecorder aviRecorder = null;

	public void setup()
	{
		new Thread(new Runnable() {
			public void run()
			{
				if (initialize())
					initialized = true;
			}
		}).start();
	}

	/**
	 * Initializes all components of the applet.
	 * @return True if initialization succeeds.
	 */
	public boolean initialize()
	{
		smallFont = loadFont("ArialMT-Small.vlw");

		try
		{
			stage = "Loading parameters";
			Parameters.init(this);

			stage = "Requesting data";
			request = new HttpRequest(this);
			String xmlSource = request.sendRequest();
			if (xmlSource == null)
			{
				stage = "Error in request";
				return false;
			}

			if (xmlSource.startsWith("ERROR"))
			{
				stage = "Got error from server";
				System.err.println(xmlSource);
				return false;
			}

			stage = "Parsing response";
			xmlParser = new XmlParser(xmlSource);
			TraceDataSet result = xmlParser.parse();

			if (Parameters.getZoomMode() == ZoomMode.Automatic)
			{
				leftAngle = result.getMinLongitude() - 3;
				rightAngle = result.getMaxLongitude() + 3;
				bottomAngle = result.getMinLatitude() - 3;
				topAngle = result.getMaxLatitude() + 3;
			}
			else
			{
				leftAngle = Parameters.getMinLongitude();
				rightAngle = Parameters.getMaxLongitude();
				bottomAngle = Parameters.getMinLatitude();
				topAngle = Parameters.getMaxLatitude();
			}

			stage = "Creating visualization";
			visualization = VisualizationFactory.Create(Parameters.getType(), this, result);
			visualization.init();

			stage = "Loading background";
			URL imageURL = new URL(this.getCodeBase(), "bgImages/" + Parameters.getBgImage());
			bgImage = loadImage(imageURL.toString());
			
			if (bgImage == null || bgImage.width == -1 || bgImage.height == -1)
			{
				stage = "Error while loading background image";
				System.err.println(imageURL.toString());
				return false;
			}

			if (Parameters.showGrid())
			{
				stage = "Loading coordinates";
				bg = new CoordinatesBackground(this);
				bg.calculate();
			}

			imageMode(CORNERS);

			clock = new Clock(this, Parameters.getInterval(), visualization.getTimeSlots());

			if (Parameters.aviRecord())
			{
				aviRecorder = new AVIRecorder(this, Parameters.getFps(), visualization
					.getTimeSlots()
					* Parameters.getFps() * Parameters.getInterval() / 1000);
			}

			return true;
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}

	public void draw()
	{
		background(192, 64, 0);
		stroke(255, 255, 255);
		noFill();

		if (!initialized)
		{
			textAlign(CENTER);
			text(stage, width / 2, height / 2);
			try
			{
				Thread.sleep(100);
			}
			catch (InterruptedException e)
			{
			}

			return;
		}

		image(bgImage, mapHAngle(-180), mapVAngle(90), mapHAngle(180), mapVAngle(-90));

		if (bg != null)
			bg.draw();

		if (!started)
		{
			clock.start();
			started = true;
		}
		else
			clock.update();

		int timeSlot = clock.getTimeSlot();
		float alpha = clock.getAlpha();

		visualization.draw(timeSlot, alpha);
		
		if (Parameters.aviRecord())
			try
			{
				aviRecorder.recordImage();
				
				if (!clock.isRunning())
					aviRecorder.finish();
			}
			catch (IOException e)
			{
				e.printStackTrace();
				exit();
			}
	}

	/**
	 * Map a longitude angle to a horizontal coordinate.
	 * @param angle Longitude angle.
	 * @return Matching horizontal coordinate.
	 */
	public float mapHAngle(float angle)
	{
		return map(angle, leftAngle, rightAngle, 0, width);
	}

	/**
	 * Map a latitude angle to a vertical coordinate.
	 * @param angle Latitude angle.
	 * @return Matching vertical coordinate.
	 */
	public float mapVAngle(float angle)
	{
		return map(angle, bottomAngle, topAngle, height, 0);
	}

	@Override
	public void smooth()
	{
		if (Parameters.getSmooth())
			super.smooth();
	}

	static public void main(String args[])
	{
		PApplet.main(new String[] { "--present", "--bgcolor=#666666", "--stop-color=#cccccc",
				"tau.dimes.visualization.applet.DimesVApplet" });
	}
}
